// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: declaration of CBoundNode.
//   $Id$


#ifndef __BOUNDNODE_H
#define __BOUNDNODE_H 1
#pragma once
//#define COUNT_CBOUNDNODE 1
#include "vertex.h"
#include "XFormsXFormsElement.h"
#include "COMCompare.h"

#include <set>

typedef std::set<IBindingTarget * > BindingTargetList;


class IBindingTarget;
class CBoundNode
{

private:
	//Private constructor/destructor needed for multiton and refcounted implementation
	CBoundNode(IXMLDOMNode * pNode,IXMLDOMSchemaCollection * pSchemaCache);
	virtual ~CBoundNode();

public:
#ifdef COUNT_CBOUNDNODE
	static unsigned long sm_Count;
#endif
	//multiton 'constructor'
	static CBoundNode * GetBoundNode(IXMLDOMNode *pXMLNode,IXMLDOMSchemaCollection * pSchemaCache, bool bDoNotCreate = false);

	//Sets the various properties
	void setRequired(bool bRequired);
	void setRelevant(bool bRelevant,bool bInheriting = false);
	void setReadOnly(bool bAccess,bool bInheriting = false);
	//constraint is unlike other properties in that there may
	// be more than one.
	HRESULT setConstraint(CVertex * pConstraint,bool bValue);
	//TODO:
	//It may be possible to call setValue with various 
	//	types of value. It is currently only implemented 
	//	for BSTR.
	HRESULT setValue(VARIANT vtVal);

	//As calculate switches the readonly property to true
	//	but still requires access throught the same channels
	//	as everything else, an override parameter is required
	//	to allow calculates to write into readonly nodes.
	//
	//This may need to be improved, I don't know whether calculate
	//	should be able to write into a node that is readonly by another means
	//	(ie. the readonly property being explicitly set).
	HRESULT setValue(BSTR const sVal,bool bOverride = false);

	//copies a fragment into the target node.
	HRESULT setValue(IXMLDOMDocumentFragment * pVal,bool bOverride = false);

	//For accessing the value stored in the node
	inline HRESULT getValue(BSTR * pbsData);

	//To return the current value of the various properties
	//	Required and valid just return the value of the 
	//	appropriate member variable, but exist for consistency
	//	as other tests are required by readonly and relevant.
	inline bool isValid(void) const;
	inline bool isReadonly(void) const;
	inline bool isRelevant(void) const;
	inline bool isRequired(void) const;


	//<summary>forces a validity check on the xml node</summary>
	//<returns>
	//	S_OK if a validity check was performed,
	//	S_FALSE if there is no validation to do.
	//</returns>
	//<remarks>
	//	To be called when the data in the node may have been changed 
	//	without going through this object.
	//	This could happen due to external code navigating to it through the DOM
	//	
	//</remarks>
	HRESULT validate(void);

	//This is needed -  When the data in this node change, 
	//	this is the vertex needs to be tugged out into the
	//	 pertinent dependency subgraph.
	CVertex * m_pValueVertex;
	
	//For adding potential targets to a 
	//	node, these recieve notification
	//	when the value cahnges, or when any
	//	of the properties change.
	//<param name="bDoItQuietly">
	//	whether to inform the target of current state during
	//	addTarget processing
	//</param>
	void addTarget(IBindingTarget * pTarget,bool bDoItQuietly=false);
	inline void RemoveTarget(IBindingTarget *pControl);

	inline HRESULT getNode(IXMLDOMNode **pNode) const;
	inline bool isSameNode(IXMLDOMNode *pComparisonNode) const;
	void cloneTargetList(CPtrList & rList);
	inline void addBoundChild(CBoundNode *pBoundNode);
	inline void RemoveBoundChild(CBoundNode *pBoundNode);
	void getType(BSTR * psType);
	HRESULT getP3PType(BSTR * psType);
	void putP3PType(BSTR sP3PType);
	HRESULT AddPropertyVertex(CVertex * pVertex, CVertex::EType eType);

	HRESULT putExternalType(ISchemaType * pType);
	// Given a list of QNames that represent schema types, determines whether 
	//	this node is of a type that is identical to, or derived by restriction
	//	from one of those types.
	bool isOneOfType(const SQName aTypes[],long lCount);
 
	void UpdateIndividualTarget(IBindingTarget * pTarget);
	inline void putXForms(CXFormsXFormsElement * pXForms);

	//TODO: probably make this COM.
	long CBoundNode::Release(void)
	{
		long lReturn = --m_lRefcount;
		if(0==lReturn)
			delete this;
		//it is an error to let refcount drop below 0
		ASSERT(lReturn >=0);
		return lReturn;
	}

	inline void AddRef()
	{
		++m_lRefcount;
	}
	static void ClearStoredNodes(void);
	// <summary>removes the constraint vertex pConstraintVertex from the list of constraint vertices</summary>
	HRESULT RemoveConstraint(CVertex * pConstraintVertex);
private:
	static bool IsDateType(ISchemaItem *pSchemaItem);

	//These are needeed to supply defaults, and monitor
	//	whether changes in state have occured
	//	They may be changed by properties that
	//	are not their own (inheritance). or by
	//	other properties such as calculate
	//	sets readonly to true by default.  
	//TODO: find out if calculate overrides readonly
	//	in that respect.  By which I mean, does calculate need 
	//	to set a different readonly flag.  can calculate  write into
	//	readonly nodes, even if readonly is set by other means.
	bool m_bReadOnly;
	bool m_bRelevant;
	bool m_bRequired;
	
	//these two properties may also
	//be inherited from an ancestor.
	bool m_bRelevantInherited;
	bool m_bReadOnlyInherited;
	bool m_bValueChanged;
	CComBSTR m_sType;
	//The actual DOM node
	IXMLDOMNode * m_pNode;
	//The bound node that contains the parent.
	CBoundNode * m_pBNParent;
	//A schemacache object to use to resolve SOM types.
	CComPtr<IXMLDOMSchemaCollection> m_pSchemaCache;

	//These may not be needed, just fill out the value
	//	The only reason they are needed is to make sure 
	//	we haven't tried to set the property twice.
	//	but we could do that with some bools or a bitmask
	CVertex * m_pReadOnly;
	CVertex * m_pRelevant;
	CVertex * m_pRequired;
	CVertex * m_pCalculate;

	CComBSTR m_sP3PType;

	//stores whether the element (if any) to which the node is bound
	//	can be nilled.
	VARIANT_BOOL m_bNillable;
	//Maps a constraint to its bool value (BOOL since it is cast to void in CMap)
	//	Whenever one is changed to true, we have  see if there are
	//	any falses still in the map.  If so, we are still false.
	CMapPtrToPtr m_ConstraintMap;
	//invalid and valid only fire in the event of a change to 
	//	between validity states.  Store the current state so that
	//	we know how to behave.
	bool m_bCurrentConstraintState;

	//Fetches the SOM types, based on the schema and
	//	on xsi:type attribute.
	HRESULT constructSchemaRelationships();
	//These types are resolved by 
	//constructSchemaRelationships()
	ISchemaType * m_pSchemaType;
	ISchemaType * m_pXSISchemaType;

	//This is an extra type inserted by
	//	put_externalType
	ISchemaType * m_pExternalType;
	
	//This is the list of controls and whatnot
	//	that refer to this node, and need to be
	//	informed of any changes, either in data
	//	or in model item property states.
	CTypedPtrList<CPtrList,IBindingTarget*> m_lstTargets;


	//This is a list of child nodes that are also bound nodes in 
	//	the scheme.  This is to allow inheritance of model item p
	//	properties.  The child node may or may not be bound to a control
	//	or property itself, but a descendent will be.
	CTypedPtrList<CPtrList,CBoundNode*> m_lstChildren;

	// Tests the suitability of a value based on the three schemaType members.
	HRESULT testValue(const BSTR & Value);
	// Given an ISchemaItem that corresponds to a type, gets the appropriate simple ISchemaType 
	static bool compareType(ISchemaType * pType ,const SQName aTypes[],long lCount); 
	// recursively checks base types of a type to see if they are of the requested type.
	static bool derivedFromType(ISchemaType* pType, const SQName aTypes[],long lCount);
	HRESULT getSimpleType(ISchemaItem * pItem , ISchemaType ** ppType);

	
	//This maps the IUnknown pointer of the IXMLDOMNode member
	//	to the CBoundNode object, implementation of multiton.
	static CMapPtrToPtr sm_Nodemap;
	static HANDLE sm_hNodemapMutex;
	long m_lRefcount;

	// <summary>Dispatches onvalueChanged to all targets, suggesting that they should update their values.</summary>
	void updateTargetValues(void);
	

	BindingTargetList m_lstUnWittingTargets;
	void CreateUnwittingTargetList();
	bool TargetUnwittingOfValueChanged(IBindingTarget * pTarget);
	void RemoveUnwittingTarget(IBindingTarget * pTarget);

	// This should not be a member, however this is a work around for the 
	// Bug 249. The removal of targets should only take place if the current xforms
	// session is valid.
	CXFormsXFormsElement * m_pXForms;

};

inline void CBoundNode::putXForms(CXFormsXFormsElement * pXForms)
{
	m_pXForms = pXForms;
}

inline bool CBoundNode::isValid(void) const 
{
	return m_bCurrentConstraintState;
}




inline bool CBoundNode::isReadonly(void) const
{
	return m_bReadOnly || m_bReadOnlyInherited;
}

inline bool CBoundNode::isRelevant(void) const
{
	return m_bRelevant && m_bRelevantInherited;
}

inline bool CBoundNode::isRequired(void) const
{
	return m_bRequired;
}
const CComBSTR g_bsXPathToFirstTextChildNode(L"text()");
//TODO: this is probably too big to be  inline now.
inline HRESULT CBoundNode::getValue(BSTR *pbsData)
{
	HRESULT hr = S_FALSE;
	if(m_pNode)
	{
		DOMNodeType nt;
		m_pNode->get_nodeType(&nt);
		if(NODE_ELEMENT == nt)
		{
			CComPtr<IXMLDOMNode> pFirstTextNode;
			
			m_pNode->selectSingleNode(g_bsXPathToFirstTextChildNode,&pFirstTextNode);
			if(pFirstTextNode)
			{
				hr = pFirstTextNode->get_text(pbsData);
			}
		}
		else
		{
			m_pNode->get_text(pbsData);
		}
	}
	else 
	{
		*pbsData = ::SysAllocString(L"");
	}
	return hr;
}

inline void CBoundNode::RemoveTarget(IBindingTarget *pControl) 
{
	POSITION pos = m_lstTargets.Find(pControl);
	if(pos)
		m_lstTargets.RemoveAt(pos);
}

inline HRESULT CBoundNode::getNode(IXMLDOMNode **pNode) const
{
	if(m_pNode) {
		*pNode = m_pNode;
		(*pNode)->AddRef();
		return S_OK;
	} else {
		*pNode = 0;
		return E_FAIL;
	}
}

inline bool CBoundNode::isSameNode(IXMLDOMNode *pComparisonNode) const 
{
	return ::COMCompare(m_pNode,pComparisonNode);
}

inline void CBoundNode::addBoundChild(CBoundNode *pBoundNode) 
{
	//Insert a child into the list of children.
	POSITION pos = 	m_lstChildren.Find(pBoundNode);
	if(!pos)
	{
		m_lstChildren.AddTail(pBoundNode);
	//	pBoundNode->AddRef();
		pBoundNode->setRelevant(m_bRelevant && m_bRelevantInherited,true);
		pBoundNode->setReadOnly(m_bReadOnly || m_bReadOnlyInherited,true);
	}
	else
	{
		AtlTrace("\nw:'formsPlayer.dll' - attempting to add a bound child which is already in the list.");
	}
}

inline void CBoundNode::RemoveBoundChild(CBoundNode *pBoundNode)
{
	POSITION pos = 	m_lstChildren.Find(pBoundNode);
	if(pos)
	{
		m_lstChildren.RemoveAt(pos);
//		pBoundNode->Release();
	}
}

#endif